package org.enhance.common.util.algorithm;

import org.enhance.common.model.po.GeoPoint;
import org.enhance.common.model.po.GeoPolygon;

import java.util.List;

/**
 * @author JiangGengchao
 * @classname GisAlgorithmUtil
 * @description gis算法工具
 * @date 2022-06-29
 **/
public class GisAlgorithmUtil {
    protected static double EARTH_RADIUS = 6378.14;
    protected static double EARTH_RADIUS_METER = 6378137;

    /**
     * 返回的结果，单位为：公里
     *
     * @param a
     * @param b
     * @return
     */
    public static double getDistance(GeoPoint a, GeoPoint b) {
        double alpha1 = (a.getLatitude().doubleValue() / 180) * Math.PI;
        double alpha2 = (b.getLatitude().doubleValue() / 180) * Math.PI;
        double beta1 = (a.getLatitude().doubleValue() / 180) * Math.PI;
        double beta2 = (b.getLatitude().doubleValue() / 180) * Math.PI;
        return (2 * Math
                .asin(Math.sqrt(Math.sin((beta1 - beta2) / 2) * Math.sin((beta1 - beta2) / 2) + Math.cos(beta1) * Math.cos(beta2) * Math.sin((alpha1 - alpha2) / 2) * Math.sin((alpha1 - alpha2) / 2))))
                * EARTH_RADIUS;
    }

    public static double getSimpleMathDistance(GeoPoint a, GeoPoint b) {
        double x1 = a.getLongitude().doubleValue();
        double y1 = a.getLatitude().doubleValue();
        double x2 = b.getLongitude().doubleValue();
        double y2 = b.getLatitude().doubleValue();
        return Math.sqrt(Math.pow((x1-x2), 2)+ Math.pow((y1-y2), 2));
    }

    public static double getPolygonArea(GeoPolygon polygon) {
        return radianArea(polygon.getGeoPoints());
    }
    /**
     * Calculate the approximate area of the polygon were it projected onto
     *     the earth.  Note that this area will be positive if ring is oriented
     *     clockwise, otherwise it will be negative.
     *
     * Reference:
     * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
     *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
     *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
     *
     * Returns:
     * {float} The approximate signed geodesic area of the polygon in square
     *     meters.
     */
    private static double radianArea(List<GeoPoint> points) {
        double area = 0;
        GeoPoint p1, p2, p3;
        int lowerIndex, middleIndex, upperIndex;
        int size = points.size();

        if (size > 2) {
            for (int i = 0; i < size; i++) {
                if (i == size - 2) {// i = N-2
                    lowerIndex = size - 2;
                    middleIndex = size -1;
                    upperIndex = 0;
                } else if (i == size - 1) {// i = N-1
                    lowerIndex = size - 1;
                    middleIndex = 0;
                    upperIndex = 1;
                } else { // i = 0 to N-3
                    lowerIndex = i;
                    middleIndex = i+1;
                    upperIndex = i+2;
                }
                p1 = points.get(lowerIndex);
                p2 = points.get(middleIndex);
                p3 = points.get(upperIndex);
                area += (getRadian(p3.getLongitude().doubleValue()) - getRadian(p1.getLongitude().doubleValue())) * Math.sin(getRadian(p2.getLatitude().doubleValue()));
            }

            area = area * EARTH_RADIUS_METER * EARTH_RADIUS_METER / 2;
        }

        return Math.abs(area);
    }

    private static double getRadian(double angle) {
        return angle * Math.PI / 180;
    }
}
